数据变更很少单独发生:一次转账需要从一个账户扣款并向另一个账户入账,一笔订单需要预留库存并记录付款。如果系统在中间停止,数据库就会描述一个从未存在过的世界。 事务 正是数据库对上述问题的回应——一个或多个操作的序列,数据库将其视为 单一的、不可分割的工作单元。
要么全部完成,要么全都不做
在事务内部,每个操作都归属同一个结果。数据库永远不会暴露一个只完成部分事务的最终状态。最经典的例子是银行账户之间的转账:先减少源账户余额,再增加目标账户余额。如果只有第一步成功,钱就会凭空消失;如果只有第二步成功,钱就会凭空出现。将两个步骤包装在一个事务中即可消除这两种可能。
这种全有或全无的保证称为 原子性。 提交 是使事务的更改永久生效的操作;一个从未提交的事务不会留下任何痕迹。 持久性 补上了最后一块拼图:一旦提交返回,即使机器随后立即断电,更改依然会保留。
事务是一条边界
把事务想象成围绕一组语句画出的边界,这样会更容易理解。在边界之前和之后,数据都满足结构所施加的规则。在边界内部,存在中间状态——一个账户已经扣款,而另一个账户尚未入账——但这些状态仅对本事务可见。其他会话中的后续语句不会将它们视为事实。
同时承担两项职责
因此,事务承担两项职责。第一项是 故障处理:如果进程崩溃、连接断开或某条语句引发错误,数据库能够恢复到事务开始之前的状态。第二项是 可见性控制:在工作进行期间,其他读取者不会看到未完成的结果。这两项职责相关但并不相同——原子性决定更改是否保留,而可见性由隔离性控制,即控制并发事务之间可以相互看到多少内容的设置。
将工作单元保持在同一个连接上
事务状态属于某个连接或会话,因此开启事务的语句和结束事务的提交必须在同一个连接上执行。许多驱动程序还运行在 自动提交 模式下,在这种模式下,每条语句都是独立的事务——多语句的工作单元需要关闭自动提交或显式开启事务。
